﻿namespace DomainServices.Tools
{
    using System;
    using System.CodeDom;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.Serialization;
    using System.ServiceModel.DomainServices;
    using System.ServiceModel.DomainServices.Server;

    internal static class CodeGenUtilities
    {
        private static Dictionary<Tuple<CodeNamespace, Type>, CodeTypeReference> codeTypeReferences;
        private static Dictionary<Assembly, Dictionary<string, string>> contractNamespaces;
        private const string DefaultDataContractSchema = "http://schemas.datacontract.org/2004/07/";
        private static Dictionary<Type, object[]> integralMinMaxValues;
        private static bool isVisualBasic;
        private static Dictionary<string, Dictionary<string, string>> namespaceTypeReferences;
        private static string rootNamespace;
        private static Dictionary<Type, string> typeNamespaceTranslations;
        private static bool useFullTypeNames;

        static CodeGenUtilities()
        {
            Dictionary<Type, object[]> dictionary = new Dictionary<Type, object[]>();
            dictionary.Add(typeof(byte), new object[] { (byte) 0, (byte) 0xff, (byte) 0 });
            dictionary.Add(typeof(sbyte), new object[] { (sbyte) (-128), (sbyte) 0x7f, (sbyte) 0 });
            dictionary.Add(typeof(short), new object[] { (short) (-32768), (short) 0x7fff, (short) 0 });
            dictionary.Add(typeof(ushort), new object[] { (ushort) 0, (ushort) 0xffff, (ushort) 0 });
            dictionary.Add(typeof(int), new object[] { -2147483648, 0x7fffffff, 0 });
            dictionary.Add(typeof(uint), new object[] { 0, uint.MaxValue, 0 });
            dictionary.Add(typeof(long), new object[] { -9223372036854775808L, 0x7fffffffffffffffL, 0L });
            dictionary.Add(typeof(ulong), new object[] { (ulong) 0L, ulong.MaxValue, (ulong) 0L });
            integralMinMaxValues = dictionary;
        }

        internal static CodeAttributeDeclaration CreateAttributeDeclaration(Type attributeType, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType)
        {
            if (!typeof(Attribute).IsAssignableFrom(attributeType))
            {
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resource.Type_Must_Be_Attribute, new object[] { attributeType.Name }), "attributeType");
            }
            return new CodeAttributeDeclaration(GetTypeReference(attributeType, codeGenerator, referencingType, true));
        }

        internal static CodeAttributeDeclaration CreateDataContractAttributeDeclaration(Type sourceType, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType)
        {
            CodeAttributeDeclaration declaration = CreateAttributeDeclaration(typeof(DataContractAttribute), codeGenerator, referencingType);
            string contractNamespace = GetContractNamespace(sourceType);
            string name = null;
            DataContractAttribute customAttribute = (DataContractAttribute) Attribute.GetCustomAttribute(sourceType, typeof(DataContractAttribute));
            if (customAttribute != null)
            {
                if (customAttribute.Namespace != null)
                {
                    contractNamespace = customAttribute.Namespace;
                }
                if (customAttribute.Name != null)
                {
                    name = customAttribute.Name;
                }
            }
            declaration.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(contractNamespace)));
            if (name != null)
            {
                declaration.Arguments.Add(new CodeAttributeArgument("Name", new CodePrimitiveExpression(name)));
            }
            return declaration;
        }

        internal static CodeAttributeDeclaration CreateDisplayAttributeDeclaration(CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType)
        {
            CodeAttributeDeclaration declaration = CreateAttributeDeclaration(typeof(DisplayAttribute), codeGenerator, referencingType);
            declaration.Arguments.Add(new CodeAttributeArgument("AutoGenerateField", new CodePrimitiveExpression(false)));
            return declaration;
        }

        internal static CodeAttributeDeclaration CreateEnumMemberAttributeDeclaration(MemberInfo memberInfo, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType)
        {
            CodeAttributeDeclaration declaration = CreateAttributeDeclaration(typeof(EnumMemberAttribute), codeGenerator, referencingType);
            EnumMemberAttribute customAttribute = (EnumMemberAttribute) Attribute.GetCustomAttribute(memberInfo, typeof(EnumMemberAttribute));
            if (customAttribute != null)
            {
                string str = customAttribute.Value;
                if (!string.IsNullOrEmpty(str))
                {
                    declaration.Arguments.Add(new CodeAttributeArgument("Value", new CodePrimitiveExpression(str)));
                }
            }
            return declaration;
        }

        internal static CodeTypeDeclaration CreateEnumTypeDeclaration(Type enumType, CodeDomClientCodeGenerator codeGenerator)
        {
            CodeTypeDeclaration typeDecl = CreateTypeDeclaration(enumType);
            typeDecl.IsEnum = true;
            typeDecl.TypeAttributes |= TypeAttributes.Public;
            Type enumUnderlyingType = enumType.GetEnumUnderlyingType();
            if (enumUnderlyingType != typeof(int))
            {
                typeDecl.BaseTypes.Add(new CodeTypeReference(enumUnderlyingType));
            }
            if (((DataContractAttribute) Attribute.GetCustomAttribute(enumType, typeof(DataContractAttribute))) != null)
            {
                CodeAttributeDeclaration declaration = CreateDataContractAttributeDeclaration(enumType, codeGenerator, typeDecl);
                typeDecl.CustomAttributes.Add(declaration);
            }
            string[] names = Enum.GetNames(enumType);
            Type underlyingType = Enum.GetUnderlyingType(enumType);
            for (int i = 0; i < names.Length; i++)
            {
                string name = names[i];
                CodeMemberField field = new CodeMemberField(GetTypeReference(underlyingType, codeGenerator, typeDecl), name);
                FieldInfo fieldInfo = enumType.GetField(name);
                if (fieldInfo != null)
                {
                    object rawConstantValue = fieldInfo.GetRawConstantValue();
                    object[] objArray = null;
                    integralMinMaxValues.TryGetValue(enumUnderlyingType, out objArray);
                    if (((objArray != null) && !rawConstantValue.Equals(objArray[2])) && rawConstantValue.Equals(objArray[0]))
                    {
                        field.InitExpression = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(enumUnderlyingType), "MinValue");
                    }
                    else if ((objArray != null) && rawConstantValue.Equals(objArray[1]))
                    {
                        field.InitExpression = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(enumUnderlyingType), "MaxValue");
                    }
                    else
                    {
                        field.InitExpression = new CodePrimitiveExpression(rawConstantValue);
                    }
                }
                typeDecl.Members.Add(field);
                if (((EnumMemberAttribute) Attribute.GetCustomAttribute(fieldInfo, typeof(EnumMemberAttribute))) != null)
                {
                    CodeAttributeDeclaration declaration2 = CreateEnumMemberAttributeDeclaration(fieldInfo, codeGenerator, typeDecl);
                    field.CustomAttributes.Add(declaration2);
                }
                CustomAttributeGenerator.GenerateCustomAttributes(codeGenerator, typeDecl, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeTypeMember, new object[] { ex.Message, fieldInfo.Name, typeDecl.Name, ex.InnerException.Message }), from a in fieldInfo.GetCustomAttributes(false).Cast<Attribute>()
                    where a.GetType() != typeof(EnumMemberAttribute)
                    select a, field.CustomAttributes, field.Comments);
            }
            if (enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0)
            {
                CodeAttributeDeclaration declaration3 = CreateAttributeDeclaration(typeof(FlagsAttribute), codeGenerator, typeDecl);
                typeDecl.CustomAttributes.Add(declaration3);
            }
            return typeDecl;
        }

        internal static CodeTypeDeclaration CreateTypeDeclaration(Type type)
        {
            CodeTypeDeclaration declaration = CreateTypeDeclaration(type.Name, type.Namespace);
            declaration.UserData["ClrType"] = type;
            return declaration;
        }

        internal static CodeTypeDeclaration CreateTypeDeclaration(string typeName, string typeNamespace)
        {
            CodeTypeDeclaration declaration = new CodeTypeDeclaration(typeName);
            declaration.UserData["Namespace"] = typeNamespace;
            return declaration;
        }

        internal static CodeCommentStatementCollection GenerateParamCodeComment(string paramName, string comment, bool isCSharp)
        {
            return GetDocComments("<param name=\"" + paramName + "\">" + comment + "</param>", isCSharp);
        }

        internal static CodeCommentStatementCollection GenerateReturnsCodeComment(string comment, bool isCSharp)
        {
            return GetDocComments("<returns>" + comment + "</returns>", isCSharp);
        }

        internal static CodeCommentStatementCollection GenerateSummaryCodeComment(string comment, bool isCSharp)
        {
            return GetDocComments("<summary>" + Environment.NewLine + comment + Environment.NewLine + "</summary>", isCSharp);
        }

        internal static string GetContractNamespace(Type entityType)
        {
            Dictionary<string, string> dictionary;
            string str2;
            if (!contractNamespaces.TryGetValue(entityType.Assembly, out dictionary))
            {
                dictionary = new Dictionary<string, string>();
                ContractNamespaceAttribute[] customAttributes = (ContractNamespaceAttribute[]) entityType.Assembly.GetCustomAttributes(typeof(ContractNamespaceAttribute), true);
                if (customAttributes.Length > 0)
                {
                    foreach (ContractNamespaceAttribute attribute in customAttributes)
                    {
                        if (attribute.ClrNamespace != null)
                        {
                            dictionary.Add(attribute.ClrNamespace, attribute.ContractNamespace);
                        }
                    }
                }
                contractNamespaces.Add(entityType.Assembly, dictionary);
            }
            string key = entityType.Namespace ?? string.Empty;
            if (dictionary.TryGetValue(key, out str2))
            {
                return str2;
            }
            return ("http://schemas.datacontract.org/2004/07/" + Uri.EscapeUriString(key));
        }

        internal static Type[] GetDictionaryGenericArgumentTypes(Type type)
        {
            Type type2;
            if (typeof(IDictionary<,>).DefinitionIsAssignableFrom(type, out type2))
            {
                return type2.GetGenericArguments();
            }
            return null;
        }

        internal static CodeCommentStatementCollection GetDocComments(string resourceComment, bool isCSharp)
        {
            if (resourceComment == null)
            {
                throw new ArgumentNullException("resourceComment");
            }
            CodeCommentStatementCollection statements = new CodeCommentStatementCollection();
            foreach (string str in resourceComment.Split(new string[] { Environment.NewLine }, StringSplitOptions.None))
            {
                statements.Add(new CodeCommentStatement(isCSharp ? str : (' ' + str), true));
            }
            return statements;
        }

        private static string GetSafeTypeName(string typeFullName, string containingNamespace, bool userType)
        {
            if (string.IsNullOrEmpty(typeFullName))
            {
                throw new ArgumentNullException("typeFullName");
            }
            string typeName = typeFullName;
            string typeNamespace = string.Empty;
            int length = typeFullName.LastIndexOf('.');
            if (length != -1)
            {
                typeName = typeFullName.Substring(length + 1);
                typeNamespace = typeFullName.Substring(0, length);
            }
            if (useFullTypeNames)
            {
                if (((isVisualBasic && userType) && !string.IsNullOrEmpty(rootNamespace)) && (!typeNamespace.Equals(rootNamespace, StringComparison.Ordinal) && !typeNamespace.StartsWith(rootNamespace + ".", StringComparison.Ordinal)))
                {
                    typeFullName = rootNamespace + "." + typeFullName;
                }
                return typeFullName;
            }
            if (!RegisterTypeName(typeNamespace, typeName, containingNamespace))
            {
                return typeName;
            }
            return typeFullName;
        }

        internal static CodeTypeReference GetTypeReference(string typeFullName, string containingNamespace, bool userType)
        {
            return new CodeTypeReference(GetSafeTypeName(typeFullName, containingNamespace, userType));
        }

        internal static CodeTypeReference GetTypeReference(Type type, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType)
        {
            return GetTypeReference(type, codeGenerator, referencingType, false, false);
        }

        internal static CodeTypeReference GetTypeReference(Type type, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType, bool optimizeAttributeName)
        {
            return GetTypeReference(type, codeGenerator, referencingType, optimizeAttributeName, false);
        }

        internal static CodeTypeReference GetTypeReference(Type type, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType, bool optimizeAttributeName, bool forceUseFullyQualifiedName)
        {
            string name = type.Name;
            string typeNamespace = type.Namespace;
            CodeNamespace namespace2 = codeGenerator.GetNamespace(referencingType);
            CodeTypeReference reference = null;
            if (optimizeAttributeName)
            {
                name = OptimizeAttributeName(type);
            }
            bool flag = (forceUseFullyQualifiedName || useFullTypeNames) || RegisterTypeName(typeNamespace, name, namespace2.Name);
            typeNamespace = TranslateNamespace(type, codeGenerator);
            if ((!flag && !namespace2.Name.Equals(type.Namespace)) && !string.IsNullOrEmpty(type.Namespace))
            {
                namespace2.Imports.Add(new CodeNamespaceImport(typeNamespace));
            }
            bool flag2 = !forceUseFullyQualifiedName;
            Tuple<CodeNamespace, Type> key = new Tuple<CodeNamespace, Type>(namespace2, type);
            if (!flag2 || !codeTypeReferences.TryGetValue(key, out reference))
            {
                if (flag && !string.IsNullOrEmpty(typeNamespace))
                {
                    name = typeNamespace + "." + name;
                }
                if (type.IsArray)
                {
                    reference = new CodeTypeReference(GetTypeReference(type.GetElementType(), codeGenerator, referencingType, false, forceUseFullyQualifiedName), type.GetArrayRank());
                }
                else if (type.IsGenericType)
                {
                    Type[] genericArguments = type.GetGenericArguments();
                    CodeTypeReference[] typeArguments = new CodeTypeReference[genericArguments.Length];
                    for (int i = 0; i < genericArguments.Length; i++)
                    {
                        typeArguments[i] = GetTypeReference(genericArguments[i], codeGenerator, referencingType);
                    }
                    reference = new CodeTypeReference(name, typeArguments);
                }
                else if ((type.IsPrimitive || (type == typeof(void))) || (((type == typeof(decimal)) || (type == typeof(string))) || (type == typeof(object))))
                {
                    reference = new CodeTypeReference(type);
                }
                else
                {
                    reference = new CodeTypeReference(name);
                }
                reference.UserData["ClrType"] = type;
                if (flag2)
                {
                    codeTypeReferences.Add(key, reference);
                }
            }
            return reference;
        }

        internal static void Initialize(bool isVisualBasic, bool useFullTypeNames, string rootNamespace)
        {
            CodeGenUtilities.isVisualBasic = isVisualBasic;
            CodeGenUtilities.useFullTypeNames = useFullTypeNames;
            CodeGenUtilities.rootNamespace = rootNamespace ?? string.Empty;
            contractNamespaces = new Dictionary<Assembly, Dictionary<string, string>>();
            codeTypeReferences = new Dictionary<Tuple<CodeNamespace, Type>, CodeTypeReference>();
            namespaceTypeReferences = new Dictionary<string, Dictionary<string, string>>();
            typeNamespaceTranslations = new Dictionary<Type, string>();
        }

        internal static string MakeCompliantFieldName(string fieldName)
        {
            int length = fieldName.Length;
            if (char.IsLower(fieldName[0]))
            {
                return ("_" + fieldName);
            }
            for (int i = 0; i < (length - 1); i++)
            {
                if (char.IsLower(fieldName[i + 1]))
                {
                    if (i == 0)
                    {
                        i = 1;
                    }
                    return ("_" + fieldName.Substring(0, i).ToLowerInvariant() + fieldName.Substring(i));
                }
            }
            return ("_" + fieldName.ToLowerInvariant());
        }

        internal static CodeExpression MakeDelegateCreateExpression(bool isCSharp, CodeTypeReference delegateType, string methodName)
        {
            if (!isCSharp)
            {
                return new CodeDelegateCreateExpression(delegateType, new CodeThisReferenceExpression(), methodName);
            }
            return new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), methodName);
        }

        internal static CodeExpression MakeEqual(Type clrType, CodeExpression left, CodeExpression right, bool isCSharp)
        {
            if (isCSharp)
            {
                return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.IdentityEquality, right);
            }
            if (((clrType != null) && clrType.IsGenericType) && (clrType.GetGenericTypeDefinition() == typeof(Nullable<>)))
            {
                return new CodeMethodInvokeExpression(left, "Equals", new CodeExpression[] { right });
            }
            if ((clrType != null) && clrType.IsValueType)
            {
                return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.ValueEquality, right);
            }
            if (clrType == typeof(string))
            {
                return new CodeMethodInvokeExpression(null, "String.Equals", new CodeExpression[] { left, right });
            }
            return new CodeMethodInvokeExpression(null, typeof(object).Name + ".Equals", new CodeExpression[] { left, right });
        }

        internal static CodeExpression MakeEqualToNull(CodeExpression value)
        {
            return new CodeBinaryOperatorExpression(value, CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null));
        }

        internal static CodeStatement MakeEventRaiseStatement(bool isCSharp, CodeEventReferenceExpression ev, params CodeExpression[] parameters)
        {
            CodeDelegateInvokeExpression expression = new CodeDelegateInvokeExpression(ev, parameters);
            if (isCSharp)
            {
                CodeConditionStatement statement = new CodeConditionStatement {
                    Condition = MakeNotEqualToNull(new CodeVariableReferenceExpression(ev.EventName))
                };
                statement.TrueStatements.Add(expression);
                return statement;
            }
            return new CodeExpressionStatement(expression);
        }

        internal static CodeExpression MakeNotEqual(Type clrType, CodeExpression left, CodeExpression right, bool isCSharp)
        {
            if (isCSharp)
            {
                return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.IdentityInequality, right);
            }
            return new CodeBinaryOperatorExpression(MakeEqual(clrType, left, right, isCSharp), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(false));
        }

        internal static CodeExpression MakeNotEqualToNull(CodeExpression value)
        {
            return new CodeBinaryOperatorExpression(value, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null));
        }

        private static bool NeedToPrefaceRootNamespace(Type type, CodeDomClientCodeGenerator codeGenerator)
        {
            if (type.Assembly.IsSystemAssembly())
            {
                return false;
            }
            bool flag = !codeGenerator.IsCSharp && !string.IsNullOrEmpty(codeGenerator.ClientProxyCodeGenerationOptions.ClientRootNamespace);
            bool flag2 = (type.IsEnum && codeGenerator.NeedToGenerateEnumType(type)) || Enumerable.Any<DomainServiceDescription>(codeGenerator.DomainServiceDescriptions, delegate (DomainServiceDescription dsd) {
                if (!dsd.EntityTypes.Contains<Type>(type) && !dsd.ComplexTypes.Contains<Type>(type))
                {
                    return dsd.DomainServiceType == type;
                }
                return true;
            });
            bool flag3 = string.Equals(type.Namespace, rootNamespace, StringComparison.Ordinal) || (!string.IsNullOrEmpty(type.Namespace) && type.Namespace.StartsWith(rootNamespace + ".", StringComparison.Ordinal));
            return ((flag && flag2) && !flag3);
        }

        private static string OptimizeAttributeName(Type type)
        {
            if (!useFullTypeNames && typeof(Attribute).IsAssignableFrom(type))
            {
                string name = type.Name;
                if (name.EndsWith("Attribute", StringComparison.Ordinal))
                {
                    return name.Substring(0, name.Length - 9);
                }
            }
            return type.Name;
        }

        internal static bool RegisterTypeName(Type type, string containingNamespace)
        {
            if (useFullTypeNames)
            {
                return true;
            }
            if (type.IsGenericType)
            {
                type = type.GetGenericTypeDefinition();
            }
            return RegisterTypeName(type.Namespace, type.Name, containingNamespace);
        }

        private static bool RegisterTypeName(string typeNamespace, string typeName, string containingNamespace)
        {
            if (string.IsNullOrEmpty(typeName))
            {
                throw new ArgumentNullException("typeName");
            }
            if (isVisualBasic || (containingNamespace == null))
            {
                containingNamespace = string.Empty;
            }
            if (!namespaceTypeReferences.ContainsKey(containingNamespace))
            {
                namespaceTypeReferences.Add(containingNamespace, new Dictionary<string, string>(StringComparer.Ordinal));
            }
            bool flag = false;
            string str = null;
            string str2 = string.IsNullOrEmpty(typeNamespace) ? typeName : (typeNamespace + "." + typeName);
            flag = namespaceTypeReferences[containingNamespace].TryGetValue(typeName, out str) && (str != str2);
            if (!flag && (str == null))
            {
                namespaceTypeReferences[containingNamespace].Add(typeName, str2);
            }
            return flag;
        }

        internal static string TranslateNamespace(Type type, CodeDomClientCodeGenerator codeGenerator)
        {
            string str;
            if (!typeNamespaceTranslations.TryGetValue(type, out str))
            {
                if (NeedToPrefaceRootNamespace(type, codeGenerator))
                {
                    str = string.IsNullOrEmpty(type.Namespace) ? rootNamespace : (rootNamespace + "." + type.Namespace);
                }
                else
                {
                    str = type.Namespace;
                }
                typeNamespaceTranslations[type] = str;
            }
            return str;
        }

        internal static Type TranslateType(Type type)
        {
            if (System.ServiceModel.DomainServices.BinaryTypeUtility.IsTypeBinary(type))
            {
                return typeof(byte[]);
            }
            if (!type.IsArray && System.ServiceModel.DomainServices.TypeUtility.IsPredefinedListType(type))
            {
                return typeof(IEnumerable<>).MakeGenericType(new Type[] { System.ServiceModel.DomainServices.TypeUtility.GetElementType(type) });
            }
            if (!System.ServiceModel.DomainServices.TypeUtility.IsPredefinedDictionaryType(type))
            {
                return type;
            }
            Type[] typeArguments = (from t in GetDictionaryGenericArgumentTypes(type) select TranslateType(t)).ToArray<Type>();
            return typeof(Dictionary<,>).MakeGenericType(typeArguments);
        }
    }
}

